#include "element.h"
#include <QXmlStreamReader>
#include <QScriptValue>
#include <QMetaProperty>
#include <QDebug>

#include "context.h"
#include "script.h"

Element::Element(Element *parent)
    :QObject(parent)
    ,id_(invalidID())
    ,hide_(false)
    ,need_focus_(false)
    ,has_focus_(false)
    ,font_color_(0)
    ,back_color_(0xffffff)
    ,enable_(true)
    ,wm_(NULL)
{
}

Element::~Element()
{
}

bool Element::copyFrom(const Element *e)
{
    if(!e) return false;

    rect_ = e->rect_;
    name_ = e->name_;
    path_ = e->path_;
    keys_ = e->keys_;
    need_focus_ = e->need_focus_;
    has_focus_ = e->has_focus_;
    enable_ = e->enable_;

    return true;
}

Element *Element::clone() const {
    qDebug() << "Error clone on Element";
    return NULL;
}

Element *Element::createElementByType(const QString &name)
{
    Context &ctx = Context::getGlobalContext();
    if(ctx.element_types.contains(name)){
        return ctx.element_types[name]->clone();
    }
    return NULL;
}

bool Element::loadXML(QXmlStreamReader &root, Context *context, SkipType skip)
{
    auto attrs = root.attributes();
    QString program;
    QScriptEngine *engine = Script::getInstance()->engine();

    QScriptContext *ctx = engine->currentContext();
    QString name = attrs.value("name").toString();
    if(name.isEmpty()){
        name = "__Element";
    }
    ctx->activationObject().setProperty(name, engine->newQObject(this));

    for(QXmlStreamAttribute attr:attrs){
        QString key = attr.name().toString();
        QString value = attr.value().toString();
        if(value.startsWith("{{")&&value.endsWith("}}"))
            value = value.mid(2, value.length()-4);
        else if(property(attr.name().toLatin1().data()).type() == QVariant::String){
            // if the property in this element is string
            value = QString("\"%1\"").arg(value);
        }

        if(key.startsWith("on")){ // signal and slot;
            if(!value.isEmpty()){
                QString signalName = key.mid(2);
                signalName[0] = signalName[0].toLower();

                QScriptValue handler = engine->evaluate(value);
                bool ret = qScriptConnect(this, getSignalSignature(signalName).toLatin1().data(), engine->newQObject(this), handler);
                if(!ret){
                    qWarning() << Q_FUNC_INFO << QString("Connect %1 to %2 {%3}failed!")
                                  .arg(signalName).arg(key).arg(value);
                }
            }
        }else{  // assign attribute
            program = QString("%1.%2=%3;").arg(name).arg(attr.name().toString()).arg(value);
            // Every attribute run program once, so error attribute is no effective.
            engine->evaluate(program);
        }
    }
    // height and width of rect must be setted last, or will be error.
    if(attrs.hasAttribute("width")||attrs.hasAttribute("height")){
        QString width = attrs.value("width").toString();
        QString height= attrs.value("height").toString();
        if(width.startsWith("{{")&&width.endsWith("}}"))
            width = width.mid(2, width.length()-4);
        if(height.startsWith("{{")&&height.endsWith("}}"))
            height = height.mid(2, height.length()-4);
        if(!width.isEmpty()){
            engine->evaluate(QString("%1.width=%2").arg(name).arg(width));
        }
        if(!height.isEmpty()){
            engine->evaluate(QString("%1.height=%2").arg(name).arg(height));
        }
    }
    if(skip==SkipAfterLoaded){
        root.skipCurrentElement();
    }
    qDebug() << "load" << name << this->rect();
    return true;
}

void Element::setWindowManager(WindowManager *wm)
{
    wm_ = wm;
}

WindowManager *Element::windowManager() const
{
    return wm_;
}

void Element::setPosition(int x, int y)
{
    setX(x);
    setY(y);
}

void Element::setX(int x)
{
    rect_.setX(x);
}

int Element::x() const
{
    return rect_.x();
}

void Element::setY(int y)
{
    rect_.setY(y);
}

int Element::y() const
{
    return rect_.y();
}

void Element::setSize(int w, int h)
{
    setWidth(w);
    setHeight(h);
}

void Element::setWidth(int w)
{
    rect_.setWidth(w);
}

int Element::width() const
{
    return rect_.width();
}

void Element::setHeight(int h)
{
    rect_.setHeight(h);
}

int Element::height() const
{
    return rect_.height();
}

void Element::setRect(const QRect &rect)
{
    rect_ = rect;
}

QRect Element::rect() const
{
    if(rect_.isValid())
       return rect_;
    return sizeHint();
}

QRect Element::sizeHint() const
{
    return rect_;
}

void Element::hide()
{
    if(!hide_){
        setVisiable(false);
        emit visiableChanged(false);
    }
}

void Element::show()
{
    if(hide_){
        setVisiable(true);
        emit visiableChanged(true);
    }
}

void Element::setVisiable(bool visiable)
{
    hide_ = !visiable;
}

bool Element::isShow() const
{
    return !hide_;
}

bool Element::isHide() const
{
    return hide_;
}

bool Element::isVisiable() const
{
    if(isHide()){
        return false;
    }

    Element *p = qobject_cast<Element*>(parent());
    if(p){
        return p->isVisiable();
    }

    return true;
}

bool Element::update()
{
    if(!prevUpdate())
        return false;
    if(isVisiable()){
        paintEvent();
        return true;
    }else{
        hideEvent();
        return false;
    }
}

bool Element::prevUpdate()
{
    return true;
}

void Element::setColor(quint32 color)
{
    font_color_ = color;
}

quint32 Element::color() const
{
    return font_color_;
}

void Element::setBackground(quint32 color)
{
    back_color_ = color;
}

quint32 Element::background() const
{
    return back_color_;
}

void Element::hideEvent()
{
    clearRect(rect());
}

quint32 Element::invalidID()
{
    return UINT_MAX - 1;
}

void Element::setID(quint32 id)
{
    id_ = id;
}

quint32 Element::id() const
{
    return id_;
}

void Element::setName(const QString &name)
{
    name_ = name;
}

QString Element::name() const
{
    return name_;
}

QString Element::type() const
{
    return metaObject()->className();
}

void Element::setPath(const QString &path)
{
    path_ = path;
}

QString Element::path() const
{
    return path_;
}

void Element::bindKeys(QList<int> keys)
{
    for(int k:keys){
        bindKey(k);
    }
}

QList<int> Element::bindingKeys() const
{
    return keys_;
}

void Element::bindKey(int key)
{
    if(!keys_.contains(key))
        keys_.append(key);
}

int Element::bindingKey() const
{
    return keys_.isEmpty()?-1:keys_.first();
}

bool Element::hasBindKey(int key)
{
    return keys_.contains(key);
}

void Element::setNeedFocus(bool needFocus)
{
    need_focus_ = needFocus;
}

bool Element::needFocus() const
{
    return need_focus_;
}

void Element::setFocus(bool focus)
{
    has_focus_ = focus;
}

bool Element::hasFocus()
{
    return has_focus_;
}

void Element::setEnable(bool enable)
{
    enable_ = enable;
}

bool Element::enable() const
{
    return enable_;
}

void Element::setModified(bool m)
{
    modified_ = m;
}

bool Element::modified()
{
    return modified_;
}

void Element::setKeyEventFilter(const Element::KeyEventFilter &filter)
{
    filter_ = filter;
}

void Element::keyboardEvent(int key, int value)
{
    if(!filter_||filter_(key, value))
        emit keyEvent(key, value);
}

QString Element::getSignalSignature(const QString &name) const
{
    if(name=="keyEvent"){
        return SIGNAL(keyEvent(int, int));
    }else if(name=="moveEvent"){
        return SIGNAL(moveEvent(MoveDirection));
    }else{
        return "";
    }
}

RegisterElementType::RegisterElementType(const QString &name, const Element &e)
{
    Context &ctx = Context::getGlobalContext();
    ctx.element_types.insert(name, QSharedPointer<Element>(e.clone()));
}
